#light
namespace FSharp.WebTools

open System
open System.IO
open System.Text
open System.Reflection
open System.Collections
open System.CodeDom
open System.CodeDom.Compiler
open System.Collections.Generic
open System.Security.Cryptography
open Microsoft.FSharp.Collections

(*-----------------------------------------------------------------------------------------------*)
/// Module with some useful utilities for working with lists and some other random stuff!
module Utils = 

  // Map with environment
  let rec fmap f env xs = 
    match xs with
    | []    -> env,[]
    | x::xs -> let env,x = f env x
               let env,xs = fmap f env xs
               env,x::xs

  let fold_st f l st = List.fold_left (fun st e -> f e st) st l
  
  let fold_sep elf sepf list =
    let rec fold_sept (first:bool) elf sepf list init =
      match list with 
        | [] -> init
        | e::l -> 
          let st1 = if (first = false) then sepf init else init 
          let st2 = elf e st1
          fold_sept false elf sepf l st2
    fold_sept true elf sepf list 
    
  let iter_sep elf sepf list =
    let rec iter_sept (first:bool) elf sepf list =
      match list with 
        | [] -> ()
        | e::l -> 
          if (first = false) then sepf();
          elf (e);
          iter_sept false elf sepf l in
    iter_sept true elf sepf list

  let seq_iter_sep elf sepf (ie:IEnumerable) =
    let rec iter_sept2 elf sepf (e:#IEnumerator) =
      while (e.MoveNext()) do
        sepf();
        elf e.Current;
      done in
    let rec iter_sept elf sepf (e:#IEnumerator) =
      if (e.MoveNext()) then ( elf e.Current; iter_sept2 elf sepf e; ) in
    iter_sept elf sepf (ie.GetEnumerator())
    
  let rec map_filter f l =
    match l with
    | [] -> [];
    | a::r -> match (f a) with | None -> (map_filter f r) | Some el -> el::(map_filter f r)

  let tryfind_any f (ie :> IEnumerable<'a>)  = 
    use e = ie.GetEnumerator()
    let mutable res = None 
    while (Option.is_none res && e.MoveNext()) do
      res <- (f (e.Current))
    done;
    res

(*-----------------------------------------------------------------------------------------------*)

  let getGenericTypeDef (ty:System.Type) = 
    if (ty.IsGenericType) then ty.GetGenericTypeDefinition() else ty

  let getGenericArgs (ty:System.Type) = 
    if (ty.IsGenericType) then ty.GetGenericArguments() else [| |]
    
  let makeGenericTypeDef (ty:System.Type) args = 
    if (ty.IsGenericType) then ty.MakeGenericType(args) else ty

(*-----------------------------------------------------------------------------------------------*)

  let key = [| 84uy; 89uy; 123uy; 18uy; 67uy; 46uy; 54uy; 231uy; |]
  let iv =  [| 45uy; 64uy; 211uy; 85uy; 46uy; 5uy; 161uy; 38uy; |]

  let encode (s:string) = 
    let des = new DESCryptoServiceProvider();
    let enc = des.CreateEncryptor(key, iv);
    let ms = new MemoryStream();
    using (new CryptoStream(ms, enc, CryptoStreamMode.Write)) (fun cst ->
      use sw = new StreamWriter(cst);
      sw.Write(s); )
    Convert.ToBase64String(ms.ToArray())

  let decode (s:string) = 
    let des = new DESCryptoServiceProvider();
    let dec = des.CreateDecryptor(key, iv);
    use ms = new MemoryStream();
    let bytes = Convert.FromBase64String(s);
    ms.Write(bytes, 0, bytes.Length);
    ms.Seek(0L, SeekOrigin.Begin) |> ignore;
    use cst = new CryptoStream(ms, dec, CryptoStreamMode.Read);
    use sr = new StreamReader(cst);
    sr.ReadToEnd()
